<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue响应式原理</title>
</head>
<body>
    <script>
        // 触发视图更新
        function triggerRender() {
            console.log('视图更新')
        }
        // 重新定义数组原型
        const oldArrayPrototype = Array.prototype
        // 创建对象-原型指向oldArrayPrototype，在此对象上扩展新的方法不会影响到原型方法
        const arrPrototype = Object.create(oldArrayPrototype)
        ;['push', 'pop', 'shift', 'unshift'].forEach(method => {
            arrPrototype[method] = function () {
                triggerRender()
                oldArrayPrototype[method].call(this, ...arguments)
            }
        })
        // 定义响应式函数
        function defineReactive(target, key, value) {
            // 深度监听 { xx: xx } [xx]
            observe(value)
            Object.defineProperty(target, key, {
                get() {
                    return value
                },
                set(newVal) {
                    if (newVal !== value) {
                        // 设置新值
                        value = newVal
                        // 新值为引用类型，如果不递归无法触发数据响应
                        observe(newVal)
                        // 触发视图更新
                        triggerRender()
                    }
                }
            })
        }
        // 监听函数
        function observe(target) {
            // 判断target类型，如果为非引用类型，则return
            if (typeof target !== 'object' || target == null) {
                return target
            }
            if (Array.isArray(target)) {
                target.__proto__ = arrPrototype
            }
            // 为target中的第一项定义响应式
            for (const key in target) {
                defineReactive(target, key, target[key])
            }
        }
        // 模拟收集data数据
        let data = {
            name: '张三',
            info: {
                address: '上海'
            },
            arr: [1, 2, 3]
        }
        // 监听数据
        observe(data)
        data.name = '李四'
        console.log(data.name)
        // 无法触发视图更新，原因：数据监听只监听了第一层级
        data.info.address = '北京'
        data.info.address = {
            first: '北',
            last: '京'
        }
        data.info.address.first = '上'
        // 新增、删除属性无法触发视图更新 $set $delete
        // 无法监听数组
        data.arr.push(4)
    </script>
</body>
</html>
